package org.totoro.jts;

import lombok.extern.slf4j.Slf4j;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.junit.Test;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;


/**
 * @author yhl
 */
@Slf4j
public class GeometryDemo {

    /**
     * 参考：https://www.giserdqy.com/gis/opengis/geotools/27687/geotools%E5%BA%94%E7%94%A8-jtsgeometry%E4%B9%8B%E9%97%B4%E7%9A%84%E5%85%B3%E7%B3%BB-2/
     * https://www.cnblogs.com/duanxingxing/p/6075380.html
     * https://www.cnblogs.com/duanxingxing/p/5150487.html
     * https://www.cnblogs.com/gaopeng527/p/5067321.html
     */

    private GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);

    /**
     * create a point
     *
     * @return
     */
    public Point createPoint() {
        Coordinate coord = new Coordinate(109.013388, 32.715519);
        Point point = geometryFactory.createPoint(coord);
        return point;
    }

    /**
     * create a rectangle(矩形)
     *
     * @return
     */
    public Envelope createEnvelope() {
        Envelope envelope = new Envelope(0, 1, 0, 2);
        return envelope;
    }

    /**
     * create a point by WKT
     *
     * @return
     * @throws ParseException
     */
    public Point createPointByWKT() throws ParseException {
        WKTReader reader = new WKTReader(geometryFactory);
        Point point = (Point) reader.read("POINT (109.013388 32.715519)");
        return point;
    }

    /**
     * create multiPoint by wkt
     *
     * @return
     */
    public MultiPoint createMulPointByWKT() throws ParseException {
        WKTReader reader = new WKTReader(geometryFactory);
        MultiPoint mpoint = (MultiPoint) reader.read("MULTIPOINT(109.013388 32.715519,119.32488 31.435678)");
        return mpoint;
    }

    /**
     * create a line
     *
     * @return
     */
    public LineString createLine() {
        Coordinate[] coords = new Coordinate[]{new Coordinate(2, 2), new Coordinate(2, 2)};
        LineString line = geometryFactory.createLineString(coords);
        return line;
    }

    /**
     * 通过 wkt  创建一条线
     *
     * @return
     * @throws ParseException
     */
    public LineString createLineByWKT() throws ParseException {
        WKTReader reader = new WKTReader(geometryFactory);
        LineString line = (LineString) reader.read("LINESTRING(0 0, 2 0)");
        return line;
    }

    /**
     * 创建多条线
     *
     * @return
     */
    public MultiLineString createMLine() {
        Coordinate[] coords1 = new Coordinate[]{new Coordinate(2, 2), new Coordinate(2, 2)};
        LineString line1 = geometryFactory.createLineString(coords1);
        Coordinate[] coords2 = new Coordinate[]{new Coordinate(2, 2), new Coordinate(2, 2)};
        LineString line2 = geometryFactory.createLineString(coords2);
        LineString[] lineStrings = new LineString[2];
        lineStrings[0] = line1;
        lineStrings[1] = line2;
        MultiLineString ms = geometryFactory.createMultiLineString(lineStrings);
        return ms;
    }

    /**
     * wkt 方式  创建 多条线
     *
     * @return
     * @throws ParseException
     */
    public MultiLineString createMLineByWKT() throws ParseException {
        WKTReader reader = new WKTReader(geometryFactory);
        MultiLineString line = (MultiLineString) reader.read("MULTILINESTRING((0 0, 2 0),(1 1,2 2))");
        return line;
    }

    /**
     * create a polygon(多边形) by WKT
     *
     * @return
     * @throws ParseException
     */

    public Polygon createPolygonByWKT() throws ParseException {

        WKTReader reader = new WKTReader(geometryFactory);
        Polygon polygon = (Polygon) reader.read("POLYGON((0 20,20 40,40 40,30 20,40 10,10 10,0 20))");

        Polygon polygon2 = (Polygon) reader.read("POLYGON((20 20,25 25,25 20,20 20)  )");

        // 判断 两个 图形 是否相交
        boolean intersects = polygon.intersects(polygon2);

        // 获取相交的区域
        Geometry intersection = polygon.intersection(polygon2);
        System.out.println(intersection);

        System.out.println(intersects);

        Point point = (Point) reader.read("POINT (29 20)");

        // 包含
        boolean contains = polygon.contains(point);

        System.out.println(contains);

        return polygon;
    }


    @Test
    public void createPolygonByWKT2() throws ParseException {

        WKTReader reader = new WKTReader(geometryFactory);

        Polygon polygon = (Polygon) reader.read("POLYGON((121.576483 31.207759,121.578072 31.209429,121.579364 31.210156,121.586177 31.211877,121.587336 31.212307,121.588925 31.213439,121.590999 31.214841,121.591969 31.215407,121.598652 31.217482,121.608249 31.219317,121.611188 31.219992,121.614074 31.220614,121.616825 31.220858,121.623678 31.221079,121.625729 31.221378,121.627159 31.222161,121.630128 31.223298,121.632854 31.223732,121.638496 31.224629,121.641439 31.225227,121.645489 31.225934,121.645948 31.225989,121.646380 31.226124,121.647190 31.224912,121.650076 31.220227,121.650866 31.218817,121.652400 31.215648,121.654086 31.212207,121.653934 31.211994,121.653327 31.211705,121.650167 31.209848,121.646825 31.207992,121.643863 31.206819,121.643650 31.206652,121.643681 31.206349,121.644880 31.198279,121.644986 31.193924,121.645229 31.191633,121.646310 31.182888,121.646174 31.182700,121.645806 31.182443,121.645080 31.182101,121.644285 31.181784,121.643679 31.181313,121.642910 31.181150,121.641782 31.180671,121.639467 31.179686,121.638288 31.179274,121.636938 31.178786,121.634487 31.177853,121.634017 31.177665,121.633411 31.177570,121.632899 31.177646,121.632019 31.177594,121.631506 31.177559,121.630644 31.177251,121.630106 31.176951,121.629602 31.176848,121.628270 31.176659,121.627817 31.176488,121.627587 31.176232,121.627348 31.175839,121.626021 31.175108,121.624009 31.174372,121.622540 31.174029,121.621624 31.173877,121.621430 31.173910,121.621341 31.174086,121.621922 31.177774,121.622357 31.179652,121.623331 31.181723,121.623325 31.181883,121.623201 31.182080,121.622063 31.182676,121.620854 31.182303,121.619773 31.181915,121.618058 31.180644,121.614700 31.178882,121.611524 31.177565,121.608672 31.176573,121.603839 31.174690,121.601777 31.173942,121.597007 31.172870,121.595187 31.172405,121.590610 31.171537,121.588672 31.170945,121.587487 31.170973,121.585655 31.171891,121.584901 31.172836,121.582963 31.177720,121.581563 31.182873,121.578892 31.192622,121.578413 31.194300,121.578004 31.195581,121.577961 31.196118,121.576989 31.200750,121.576554 31.202688,121.576086 31.204438,121.575967 31.205266,121.575890 31.205906,121.575975 31.206452,121.576112 31.206990,121.576483 31.207759))");

        int limit = 3;

        Envelope envelopeInternal = polygon.getEnvelopeInternal();


        double offsetX = envelopeInternal.getMinX();
        double offsetY = envelopeInternal.getMinY();


        double width = envelopeInternal.getWidth() / limit;

        double height = envelopeInternal.getHeight() / limit;


        ArrayList<Envelope> envelopes = new ArrayList<>();


        String tpl = "POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))";

        for (int i = 0; i < limit; i++) {

            for (int i1 = 0; i1 < limit; i1++) {


                Double x1 = this.conv(offsetX + (width * i));
                Double x2 = this.conv(offsetX + (width * (i + 1)));

                Double y1 = this.conv(offsetY + (height * i1));
                Double y2 = this.conv(offsetY + (height * (i1 + 1)));

                Envelope envelope = new Envelope(x1, x2, y1, y2);

                Polygon polygon2 = (Polygon) reader.read(String.format(tpl, x1, y2, x2, y2, x2, y1, x1, y1, x1, y2));

                System.out.println(toGaodePolygon(polygon2));

                log.info("{}|{}", x1 + "," + y2, x2 + "," + y1);


                envelopes.add(envelope);

            }
        }

        System.out.println(polygon);
    }

    public String toGaodePolygon(Polygon polygon) {

        Coordinate[] coordinates = polygon.getCoordinates();

        String gaode = Arrays.asList(coordinates).stream().map(e -> {
            return e.getX() + "," + e.getY();
        }).reduce((e1, e2) -> {
            return e1 + "|" + e2;
        }).get();

        return gaode;
    }

    private Double conv(Double value) {
        return BigDecimal.valueOf(value).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 创建 多个 多边形
     *
     * @return
     * @throws ParseException
     */
    public MultiPolygon createMulPolygonByWKT() throws ParseException {
        WKTReader reader = new WKTReader(geometryFactory);
        MultiPolygon mpolygon = (MultiPolygon) reader.read("MULTIPOLYGON(((0 20,20 40,40 40,30 20,40 10,10 10,0 20),(20 20,25 25,25 20,20 20) ))");
        return mpolygon;
    }

    @Test
    public void testcreateMulPolygonByWKT() throws ParseException {
        this.createMulPolygonByWKT();
    }


    /**
     * create GeometryCollection  contain point or multiPoint or line or multiLine or polygon or multiPolygon
     *
     * @return
     * @throws ParseException
     */
    @Test
    public void createGeoCollect() throws ParseException {
        LineString line = createLine();
        Polygon poly = createPolygonByWKT();
        Geometry g1 = geometryFactory.createGeometry(line);
        Geometry g2 = geometryFactory.createGeometry(poly);
        Geometry[] garray = new Geometry[]{g1, g2};
        GeometryCollection gc = geometryFactory.createGeometryCollection(garray);


        System.out.println(gc);

    }

    /**
     * create a Circle  创建一个圆，圆心(x,y) 半径RADIUS
     *
     * @param x
     * @param y
     * @param RADIUS
     * @return
     */
    public Polygon createCircle(double x, double y, final double RADIUS) {
        final int SIDES = 32;//圆上面的点个数
        Coordinate coords[] = new Coordinate[SIDES + 1];
        for (int i = 0; i < SIDES; i++) {
            double angle = ((double) i / (double) SIDES) * Math.PI * 2.0;
            double dx = Math.cos(angle) * RADIUS;
            double dy = Math.sin(angle) * RADIUS;
            coords[i] = new Coordinate((double) x + dx, (double) y + dy);
        }
        coords[SIDES] = coords[0];
        LinearRing ring = geometryFactory.createLinearRing(coords);
        Polygon polygon = geometryFactory.createPolygon(ring, null);
        return polygon;
    }

    /**
     * @param args
     * @throws ParseException
     */
    public static void main(String[] args) throws ParseException {
        GeometryDemo gt = new GeometryDemo();
        Polygon p = gt.createCircle(0, 1, 2);
        //圆上所有的坐标(32个)
        Coordinate coords[] = p.getCoordinates();
        for (Coordinate coord : coords) {
            System.out.println(coord.x + "," + coord.y);
        }
        Envelope envelope = gt.createEnvelope();
        System.out.println(envelope.centre());
    }


}